<template>
	<view class="page" :initdata="initdata" :change:initdata="ether.getalldata" :beforedata="beforedata"
		:change:beforedata="ether.beforedatachange">
		<HeaderBar :title="title" showBack />
		<web-view :src="src+`?address=${initdata.address}&chainid=${initdata.chainid}&fullHost=${initdata.fullHost}`"
			@message="getMessage" :getdata="getdata" :change:getdata="ether.zhixing" :fullscreen="false"
			:webview-styles="webstyles"></web-view>
	</view>
</template>
<script>
	import common from '@/js_sdk/common/common.js'
	var subNVue;
	export default {
		data() {
			return {
				// 源地址
				oldurl: "",
				// 当前显示的页面链接
				src: "",
				// 当前的webView对象
				webView: '',
				// webview的样式，高度在下面通过JS动态获取
				webstyles: {
					top: "0px"
				},
				// 用于驱动获取弹窗页的数据
				beforedata: null,
				// 用于驱动获取执行后的数据
				getdata: null,
				// 页面标题
				title: ""
			}
		},
		computed: {
			// renderjs层需要的信息
			initdata() {
				return {
					privateKey: this.$store.getters.addressInfo.privateKey,
					fullHost: this.$store.getters.getRpc,
					address: this.$store.getters.addressInfo.address,
					chainid: this.$store.getters.addressInfo.chainId,
					config: common.config,
					src: this.src
				}
			}
		},
		// 首先是上个页面的参数只能通过onload来获取
		// 其次是想在页面挂载前把webview的高度搞定，以免卡顿
		onLoad(option) {
			let this_ = this
			uni.getSystemInfo({
				success: function(res) {
					// console.log(res);
					let top = res.statusBarHeight + uni.upx2px(80)
					let height = res.windowHeight - top
					this_.webstyles.top = top + "px"
					this_.webstyles.height = height + "px"
				}
			});
			this_.oldsrc = option.url
			this_.search(this_.oldsrc)
			// 将原数据的参数进行处理
			let srcarr = option.url.split("?")
			if (srcarr.length <= 1) {
				this_.src = option.url +
					`?chainid=${this.initdata.chainid}&address=${this.initdata.address}&fullHost=${this.initdata.fullHost}`
			} else {
				this_.src = option.url +
					`&chainid=${this.initdata.chainid}&address=${this.initdata.address}&fullHost=${this.initdata.fullHost}`
			}
			console.log(this_.src)

		},
		mounted() {
			// 这个showloading是配合ETH对象注入的，在注入成功前不想让用户操作
			// 会显示很短的时间
			uni.showLoading({
				title: "加载中",
				mask: true,
			})
			// 获取弹窗并设置大小和位置
			subNVue = uni.getSubNVueById('ethconcat')
			subNVue.setStyle({
				position: "absolute", //除 popup 外，其他值域参考 5+ webview position 文档
				height: "100%",
				width: "100%",
				background: "transparent",
				bottom: "0px"
			})
			subNVue.hide()
			// 动态获取webview对象，因为经测试发现webview不是一开始就能获取到的
			this.setInterval()
			setTimeout(function() {
				uni.hideLoading()
			}, 3000)
		},
		methods: {
			...common.methonds,
			setInterval() {
				let currentWebview = this.$scope.$getAppWebview();
				this.webView = currentWebview.children()[1];
				if (this.webView && this.initdata) {
					let this_ = this
					this.webView.onloaded = function(e) {
						this_.webView.appendJsFile("_www/static/script/uni.js");
						this_.webView.appendJsFile("_www/static/script/trust.js");
						this_.webView.appendJsFile("_www/static/script/eth.js");
						// console.log(this_.initdata)
						// let evalJS = `
						// 		init('${this_.initdata.address}','${this_.initdata.chainid}','${this_.initdata.fullHost}')
						// 	`
						// this_.webView.evalJS(evalJS);
					}
				} else {
					let this_ = this
					setTimeout(this_.setInterval, 100)
				}
			},
			search(res) {
				let cleanUrl = res.split('?')[0]; // 移除查询参数
				cleanUrl = cleanUrl.split('#')[0]; // 移除哈希部分
				uni.request({
					url: cleanUrl,
					method: 'GET',
					success: (response) => {
						let htmlText = response.data;
						// 使用正则表达式提取标题
						let titleMatch = htmlText.match(/<title[^>]*>([^<]+)<\/title>/);
						let title = titleMatch ? titleMatch[1] : 'No Title';
						this.title = title
					},
					fail: (error) => {
						console.error('Error fetching the URL:', error);
					}
				});
			},
			// 单独的等待结果方法
			awaitretrundata(id) {
				return new Promise((resolve, reject) => {
					uni.$once("returndata", function(e) {
						if (e.id == id) {
							subNVue.hide()
							resolve(e.status);
						}
					})
				})
			},
			// 获取到webview传过来的数据的方法
			async getMessage(e) {
				let data = e.detail.data[0]
				// ETH对象注入成功后隐藏加载框
				if (data.handler == "hideloading") {
					uni.hideLoading()
				}
				// 目前发现了DAPP浏览器回合钱包进行4种交互，这三种的处理逻辑判断在rendjs层
				else if (data.handler == "signPersonalMessage" || data.handler == "signTransaction" || data
					.handler ==
					"signTypedMessage") {
					uni.showLoading({
						title: "加载中",
						mask: true,
					})
					this.beforedata = data
				}
				// 这个主要针对的是获取地址那个方法
				else {
					this.getdata = data
				}
			},
			// 打开底部弹窗
			async opendibu(data) {
				uni.hideLoading()
				subNVue.show('slide-in-bottom', 200, () => {
					uni.$emit("openconcat", data.res)
				})
				// 获取执行结果
				let returnstatus = await this.awaitretrundata(data.res.id)
				if (returnstatus) {
					this.getdata = data.newvalue
				} else {
					let errordata = {
						message: "Confirmation declined by user",
						newvalue: data.newvalue
					}
					this.returnerror(errordata)
				}
			},
			// 获取到rendjs层执行后的结果，返回给dapp
			returndata(data) {
				let newdata = JSON.stringify(data.returndata)
				let returndata = `
						window.ethereum.sendResponse(${data.id},${newdata})
						`
				this.webView.evalJS(returndata)
			},
			// 报错要报到DAPP里，所以这样执行了
			returnerror(data) {
				let returndata = `
						window.ethereum.sendError(${data.newvalue.id},'${data.message}')
						`
				this.webView.evalJS(returndata)
			},
			// rendjs层开始处理弹窗页的数据时，清空beforedata的值
			initbeforedata() {
				this.beforedata = null
			},
			//rendjs层开始执行DAPP的逻辑时，清空 getdata的值
			initgetdata() {
				this.getdata = null
			},

		}
	}
</script>
<script module="ether" lang="renderjs">
	var provider, signer;
	export default {
		data() {
			return {
				loadstatus: false,
				datastatus: false,
				privateKey: null,
				fullHost: null
			}
		},
		mounted() {
			let script = document.createElement('script')
			script.src = 'static/script/ethers-5.2.umd.min.js'
			script.onload = this.changeloadstatus
			document.head.appendChild(script)
		},
		methods: {
			// JS 文件引入成功后修改引入状态
			changeloadstatus() {
				this.loadstatus = true
				if (this.datastatus == true) {
					this.initethWeb()
				}
			},
			// 私钥和rpc更新过来后修改数据状态
			// 这样处理的原因是因为两者执行顺序不确定
			// 所有两者都准备好后，由后准备好的数据来触发构建节点方法
			getalldata(newValue, oldValue, ownerVm, vm) {
				if (newValue && newValue != undefined) {
					this.privateKey = newValue.privateKey
					this.fullHost = newValue.fullHost
					this.address = newValue.address
					this.config = newValue.config
					this.src = newValue.src
					this.datastatus = true
					if (this.loadstatus == true) {
						this.initethWeb()
					}
				}
			},
			// 构建节点的方法
			async initethWeb() {
				provider = new ethers.providers.JsonRpcProvider(this.fullHost);
				signer = new ethers.Wallet(this.privateKey, provider);
			},
			async beforedatachange(newvalue) {
				if (newvalue) {
					this.$ownerInstance.callMethod('initbeforedata')
					this.$ownerInstance.callMethod('showloading')
					try {
						let res;
						if (newvalue.handler == "signTransaction") {
							// 这三个是基础参数，肯定会有的
							let newdata = {
								value: newvalue.data.value,
								from: newvalue.data.from,
								to: newvalue.data.to
							}
							// 判断一下有没有value，有就加上，没有就不管了
							if (newvalue.data.value) {
								newdata.value = newvalue.data.value
							}
							// 这里先判断有没有data，有的话就加上，因为下一步要获取gas了
							if (newvalue.data.data) {
								newdata.data = newvalue.data.data
							}
							// 获取gas,如果有的话就直接用,没有的话就获取一下然后赋值
							if (newvalue.data.gas) {
								newdata.gasLimit = ethers.BigNumber.from(newvalue.data.gas)
							} else {
								let gaslimit = await signer.estimateGas(newdata)
								newdata.gasLimit = gaslimit
							}
							// 获取gasprice，如果有的话就直接用，没有的话就获取一下然后赋值
							if (newvalue.data.gasPrice) {
								newdata.gasPrice = ethers.BigNumber.from(newvalue.data.gasPrice)
							} else {
								let gasprice = await signer.getGasPrice()
								newdata.gasPrice = gasprice
							}

							let feecost = newdata.gasLimit.toNumber() * newdata.gasPrice.toNumber()
							let fee = ethers.utils.formatUnits(feecost.toString(), "ether")
							// 至此获取到了交易的gas消耗和gas手续费,以及他们价值多少主币

							// 优化代码，最终返回值为同一个变量
							// 如果有data就开始解析data的数据
							if (newdata.data) {
								let parsing = new ethers.utils.Interface(this.config.erc20abi)
								let methodid = newdata.data.slice(0, 10)
								if (methodid == "0xa9059cbb" || methodid == "0x095ea7b3") {
									// 构建合约来获取代币的小数位和符号
									// 这里面有个前提就是调用这两个方法的话就一定是ERC20代币合约，不然获取余额和小数位的方法就不存在了
									let contract = new ethers.Contract(newdata.to, this.config.erc20abi,
										signer);
									let symbol = await contract.symbol()
									let decimals = await contract.decimals()
									// 解析参数
									let parameter = parsing.decodeFunctionData(methodid, newdata.data)
									// 至此，我们已经获取到了代币符号，发送者和接收者了。
									res = {
										from: newdata.from,
										to: parameter[0],
										value: ethers.utils.formatUnits(parameter[1].toString(), decimals),
										symbol: symbol,
										gaslimit: newdata.gasLimit.toString(),
										gasprice: ethers.utils.formatUnits(newdata.gasPrice.toString(), "gwei"),
										fee: fee,
										methodid: methodid,
										id: newvalue.id,
										handler: newvalue.handler,
										src: this.src
									}
								}
							} else {
								res = {
									value: ethers.utils.formatUnits(newdata.value, "ether"),
									gaslimit: newdata.gasLimit.toString(),
									gasprice: ethers.utils.formatUnits(newdata.gasPrice, "gwei"),
									from: newdata.from,
									to: newdata.to,
									id: newvalue.id,
									fee: fee,
									methodid: "",
									handler: newvalue.handler
								}

							}
						} else if (newvalue.handler == "signPersonalMessage") {
							let data = ethers.utils.toUtf8String(newvalue.data.data)
							let address = newvalue.data.address
							res = {
								data,
								address,
								id: newvalue.id,
								handler: newvalue.handler
							}

						} else if (newvalue.handler == "signTypedMessage") {
							res = {
								data: newvalue.data.data,
								raw: newvalue.data.raw,
								address: newvalue.data.address,
								id: newvalue.id,
								handler: newvalue.handler
							}
						}
						this.$ownerInstance.callMethod('opendibu', {
							res,
							newvalue
						})

					} catch (e) {
						this.$ownerInstance.callMethod('error', "合约执行失败")
						this.$ownerInstance.callMethod('returnerror', {
							message: e.message,
							newvalue
						})
					}
					this.$ownerInstance.callMethod('hideloading')

				}
			},
			// 钱包接收DAPP的所有请求都会在这里执行。
			async zhixing(newValue) {
				if (newValue && newValue != undefined) {
					this.$ownerInstance.callMethod('showloading')
					try {
						this.$ownerInstance.callMethod('initgetdata')
						let returndata;
						if (newValue.handler == "requestAccounts") {
							returndata = [this.address]
						} else if (newValue.handler == "signPersonalMessage") {
							returndata = await signer.signMessage(ethers.utils.toUtf8String(newValue.data.data))
						} else if (newValue.handler == "signTypedMessage") {
							let raw = JSON.parse(newValue.data.raw)
							// 这个是多出来的部分，加上就成功不了,删掉就好了,很奇怪
							delete raw.types.EIP712Domain
							returndata = await signer._signTypedData(raw.domain, raw.types, raw.message);
						} else if (newValue.handler == "signTransaction") {
							// newValue中的燃气消耗数量是gas，不是gaslimit,这个会导致后续方法报错
							// 个人觉得是因为是两个不同的包导致的暂时没有好的办法，先这样
							let newdata = {
								gasLimit: newValue.data.gas,
								value: newValue.data.value,
								from: newValue.data.from,
								to: newValue.data.to
							}
							if (newValue.data.data) {
								newdata.data = newValue.data.data
							}
							let res = await signer.sendTransaction(newdata)
							returndata = res.hash
						}
						this.$ownerInstance.callMethod('returndata', {
							returndata,
							id: newValue.id
						})
					} catch (e) {
						this.$ownerInstance.callMethod('error', "合约执行失败")
						this.$ownerInstance.callMethod('returnerror', {
							message: e.message,
							newvalue
						})
					}
					this.$ownerInstance.callMethod('hideloading')
				}
			}
		}
	}
</script>

<style lang='less' scoped>
	.page {
		box-sizing: border-box;
		width: 100vw;
		height: 100vh;
		display: flex;
		flex-direction: column;
		background-color: #F6F6F6;

		.tanchuang {
			width: 100vw;
			height: 50vh;
			background-color: #000;
			position: absolute;
			left: 0px;
			bottom: 0px;
			right: 0px;
		}
	}
</style>